// kheap.c -- Kernel heap functions, also provides
//            a placement malloc() for use before the heap is 
//            initialised.
//            Written for JamesM's kernel development tutorials.

#include "headers/kheap.h"
#include "headers/paging.h"
#include "../tools/headers/common.h"
#include "../api/headers/errors.h"
#include "../adt/headers/heap.h"
#include "../api/headers/string.h"


// end is defined in the linker script.
extern u32int end;
u32int placement_address = (u32int)&end;
extern page_directory_t *kernel_directory;
heap_t *kheap=0;
extern volatile BOOL critical_section;

/**
	Kernel memory allocation. Allocates specified amount of memory and returns a pointer to it.
	Kheap will be used if initialized, and placement_address memory management otherwise.
	phys - map to physicall address. Calls panic if memory not found.
 */
u32int kmalloc_int(u32int sz, int align, u32int *phys)
{
	BOOL backup = critical_section;
	critical_section = true;
	setLastError(NO_ERROR);
    if (kheap != 0 )
    {
        void *addr = alloc(sz, (u8int)align, kheap);
		if(getLastError() != NO_ERROR)
		{
			critical_section = backup;
			return 0;
		}
        if (phys != 0)
        {
            page_t *page = get_page((u32int)addr, 0, kernel_directory);
            *phys = page->frame*0x1000 + ((u32int)addr & 0xFFF);
        }
        critical_section = backup;
        return (u32int)addr;
    }
    else //Before heap is initialized
    {
        if (align && (placement_address & 0xFFF) )
        {
            // Align the placement address;
            placement_address &= 0xFFFFF000;
            placement_address += 0x1000;
        }
        if (phys)
        {
            *phys = placement_address;
        }
        u32int tmp = placement_address;
        placement_address += sz;
		critical_section = backup;
        return tmp;
    }
    
}

/**
	Frees memory, allocated in kernel heap.
 */
void kfree(void *p)
{
	BOOL backup = critical_section;
	critical_section = true;
    free(p, kheap);
	critical_section = backup;
}
/**
	kmalloc alligned
 */
u32int kmalloc_a(u32int sz)
{
    return kmalloc_int(sz, 1, 0);
}

/**
	kmalloc with physical address return (not aligned)
 */
u32int kmalloc_p(u32int sz, u32int *phys)
{
    return kmalloc_int(sz, 0, phys);
}

/**
	kmalloc with physical addres return (aligned)
 */
u32int kmalloc_ap(u32int sz, u32int *phys)
{
    return kmalloc_int(sz, 1, phys);
}

/**
   Not aligned, no physical address return.
 */ 
u32int kmalloc(u32int sz)
{
    return kmalloc_int(sz, 0, 0);
}

